跳到主要内容

SpringMVC 响应数据

数据响应

这里就介绍直接返回数据的方法,至于视图那部分就跳过了,反正前后端分离之后也用不上

因为 SpringMVC 默认还是要走视图解析器,所以需要加上 @ResponseBody 注解,告诉 Spring 当前的 Service 跳过视图解析器,直接返回响应体

加上这个 @ResponseBody 注解后,return 的数据就是响应体的数据

前后端分离的核心,前端需要后端提供的数据,介质就是通过 json 来传输

所以 return 的数据就是 json,一般不需要手动将 java 对象转成 json 数据,正常开发都是使用一些第三方工具(例如 gson、fastjson、jackson)

Gson 自动返回 Json

导入依赖

<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>

Gson 的使用

private static final Gson gson = new Gson();

@GetMapping(value = "/json",produces = "text/json;charset=utf-8")
@ResponseBody
public String test2(){
User user = new User(1,"小红",20);
return gson.toJson(user);
}

集成到 Spring

通过添加对消息转换器 @ResponseBody 的配置可以达到自动配置编码以及自动把对象转换成 json 发送出去,无需手动处理

注意这个 message-converters 作用就是:配置一个或多个 HTTP 消息转换器来使用用于转换 @RequestBody@ResponseBody 注解所修饰的 Controller 方法的返回值

实际就是 对注解解析添加一些工具(例如这里的消息转换器)

<!-- 配置这个注解驱动,如果直接使用 <mvc:annotation-driven> 则默认使用 jackson 来把对象转换成 json -->
<!-- 这个 <mvc:annotation-driven> 默认设置了很多东西,可以看上一篇的介绍 -->
<mvc:annotation-driven>
<!--之所以需要在这里配置是因为这个信息转换器是针对 @ResponseBody 注解的-->
<mvc:message-converters>
<!--json 乱码问题配置-->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="utf-8"/>
</bean>

<!--配置 json 映射的工具,这里使用 GSON-->
<bean class="org.springframework.http.converter.json.GsonHttpMessageConverter">
<property name="gson">
<bean class="com.google.gson.Gson"/>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

配置好 消息转换器 后就可以解放双手,直接传出对象自动生成 json 并自动转换为 utf-8

@GetMapping("/json")
@ResponseBody
public User test2(){
return new User(1,"小红",20);
}

甚至可以自动识别 List 生成数组

@GetMapping("/json")
@ResponseBody
public List<User> test2(){
List<User> users = new ArrayList<>();
users.add(new User(1,"小红",20));
users.add(new User(2,"艾达",30));
users.add(new User(3,"里昂",23));
return users;
}

ResponseEntity 工具类

参考资料 【SpringMVC(十三)】ResponseEntity 使用 及 原理

ResponseEntity 是什么

通常情况下,在前后端分离的大背景下,我们后台服务返回给前端的通常都是格式化的数据,比如 Json,开始的时候,我们用 json包生产一个 json的字符串,配合 http 协议的一些 API 来自定义实现,后来 SpringMVC 包装出来了通用的处理类 ResponseEntity

ResponseEntity 继承了 HttpEntity 类,HttpEntity 代表一个 http 请求或者响应实体,其内部有两个成员变量:header 及 body,代表 http 请求或响应的 header 及 body,其中的 body 是泛化的。

public class ResponseEntity<T> extends HttpEntity<T> {

private final Object status;

public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatus status) {
super(body, headers);
Assert.notNull(status, "HttpStatus must not be null");
this.status = status;
}

// 省略其他代码
}

ResponseEntity 基本使用

@RequestMapping(value="/demo1" )
public ResponseEntity demo1(){
// 使用方式一.
ResponseEntity responseEntity = new ResponseEntity(new User("lvbb",24),HttpStatus.OK);

// 使用方式二.
return ResponseEntity.ok(new User("lvbb",24));
}

ResponseEntity 可以作为 controller 的返回值,比如对于一个处理下载二进制文件的接口,可以这么定义:

@RequestMapping("/download")
public ResponseEntity<byte[]> download(@RequestParam String fileName) throws IOException {
byte[] bytes = xxx;
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
}

也可以使用流式风格的构造器:

@RequestMapping("/download")
public ResponseEntity<byte[]> download(@RequestParam String fileName) throws IOException {
byte[] bytes = xxx;
return ResponseEntity.ok()
.headers(headers)
.body(bytes);
}

ResponseEntity 不仅仅可以用于处理下载,非 ModelAndView 的其他场景均可以使用。在 WEB API 项目中,如果没有特定的 Java Bean 封装的返回类型,可以使用该类型。

与 @ResponseBody 的区别

使用 ResponseEntity 作为 controller 的返回值,我们可以方便地处理响应的 header,状态码以及 body。而通常使用的 @ResponseBody 注解,只能处理 body 部分。这也是为什么通常在下载场景中会使用 ResponseEntity,因为下载需要设置 header 里的 content-type 以及特殊的 status(比如206)。

ResponseEntity 的优先级高于 @ResponseBody。在不是 ResponseEntity 的情况下才去检查有没有 @ResponseBody 注解。如果响应类型是ResponseEntity可以不写 @ResponseBody 注解,写了也没有关系。

简单粗暴的讲

  • @ResponseBody 可以直接返回 Json 结果
  • ResponseEntity 不仅可以返回 Json 结果,还可以定义返回的 HttpHeaders 和 HttpStatus

使用 ResponseEntity 自定义响应

使用这个 ResponseEntity 自定义 Restful 风格的响应内容

ResponseEntity 的用法(也是建造者模式)

ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
ResponseEntity.ok(list);
ResponseEntity.badRequest().build();
ResponseEntity.notFound().build();

创建 Result 类

创建一个 Result 类(建造者模式)

@Data
@Builder
public class Result<T> {
/**
* 业务码,比如成功、失败、权限不足等 code,可自行定义
*/
@ApiModelProperty("返回码")
private Integer code;
/**
* 返回信息,后端在进行业务处理后返回给前端一个提示信息,可自行定义
*/
@ApiModelProperty("返回信息")
private String message;
/**
* 数据结果,泛型,可以是列表、单个对象、数字、布尔值等
*/
@ApiModelProperty("返回数据")
private T data;
}

编写通用生成器工具类

再创建一个通用类型生成器,省的每次创建还需要手动设置状态码(对于复杂的返回类型依旧可以手动生成)

public final class ResultGeneratorUtils {

private ResultGeneratorUtils(){}

private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS";
private static final int RESULT_CODE_SUCCESS = 200;

public static Result<String> genSuccessResult() {
return Result.<String>builder()
.code(RESULT_CODE_SUCCESS)
.message(DEFAULT_SUCCESS_MESSAGE)
.build();
}

public static Result<String> genSuccessResult(String message) {
return Result.<String>builder()
.code(RESULT_CODE_SUCCESS)
.message(message)
.build();
}

public static <T> Result<T> genSuccessResult(T data) {
return Result.<T>builder()
.code(RESULT_CODE_SUCCESS)
.message(DEFAULT_SUCCESS_MESSAGE)
.data(data)
.build();
}

public static <T> Result<T> genSuccessResult(String message, T data) {
return Result.<T>builder()
.code(RESULT_CODE_SUCCESS)
.message(message)
.data(data)
.build();
}
}

最后使用方式

@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
@ApiOperation("说你好,需要 ADMIN 权限")
@GetMapping("/hello")
public ResponseEntity<Result<String>> sayHello() {
return ResponseEntity
.ok()
.body(ResultGeneratorUtils.genSuccessResult("你好,你能看到这条内容表示权限系统差不多了"));
}